//! gameboy serial stuff

// serial callback: fn style: (u8) -> Option<u8> and trait: Send + 'a
pub type SerialFn<'a> = Box<dyn FnMut(u8) -> Option<u8> + Send + 'a>;

pub struct Serial<'a> {
    data: u8,
    control: u8,
    callback: SerialFn<'a>,

    pub interrupt: u8,
}

impl<'a> Serial<'a> {
    pub fn new_with_callback(cb: SerialFn<'a>) -> Self {
        Serial {
            data: 0,
            control: 0,
            callback: cb,
            interrupt: 0,
        }
    }

    // write bytes ?
    pub fn wb(&mut self, addr: u16, value: u8) {
        match addr {
            0xFF01 => self.data = value,
            0xFF02 => {
                self.control = value;
                if value & 0x81 == 0x81 {
                    let callback_run = (self.callback)(self.data);
                    match callback_run {
                        Some(v) => {
                            self.data = v;
                            self.interrupt = 0x8;
                        }

                        None => {}
                    }
                }
            }

            _ => unimplemented!("Serial doesn't handle address: {:4X} (write)", addr),
        }
    }

    // read bytes ?
    pub fn rb(&self, addr: u16) -> u8 {
        match addr {
            0xFF01 => self.data,
            0xFF02 => self.control | 0b0111_1110,

            _ => {
                //unimplemented!("Serial doesn't handle address {:4X} (read)", addr);
                0
            }
        }
    }

    pub fn set_callback(&mut self, cb: SerialFn<'static>) {
        self.callback = cb;
    }

    pub fn unset_callback(&mut self) {
        self.callback = Box::new(noop);
    }
}

impl Serial<'static> {
    pub fn new() -> Self {
        Self {
            data: 0,
            control: 0,
            callback: Box::new(noop),
            interrupt: 0,
        }
    }
}

// no callback
fn noop(_: u8) -> Option<u8> {
    None
}
